#pragma once

#include <iostream>
#include <queue>
#include <pthread.h>

// 缓冲区默认容量
static const int default_capacity = 5;

//生产消费者模型类
template <class T>
class BlockQueue
{
private:
    std::queue<T> _task_queue; // 任务队列
    int _capacity;             // 队列容量
    pthread_mutex_t _mutex;    // 锁
    pthread_cond_t _c_cond;    // customer条件变量
    pthread_cond_t _p_cond;    // productor条件变量

public:
    // 构造函数
    BlockQueue(int capacity = default_capacity)
        :_capacity(capacity)
    {
        //初始化锁和条件变量
        pthread_mutex_init(&_mutex, nullptr);
        pthread_cond_init(&_c_cond, nullptr);
        pthread_cond_init(&_p_cond, nullptr);
    }
    //生产者--将任务放入任务队列
    void Push(const T& task)
    {
        //加锁
        pthread_mutex_lock(&_mutex);
        //判断缓冲区是否满了,如果满了的话，就一直通知消费者取出数据
        while(_capacity == _task_queue.size())
        {
            //释放锁，并等待消费者通知可以生产
            pthread_cond_wait(&_p_cond, &_mutex);
        }
        //有空间了，放入任务
        _task_queue.push(task);
        //通知消费者可以取数据了
        pthread_cond_signal(&_c_cond);
        //解锁
        pthread_mutex_unlock(&_mutex);
    }
    //消费者--将任务从任务队列取出
    void PoP(T* task)
    {
        //加锁
        pthread_mutex_lock(&_mutex);
        //判断是否有数据，没有就进行等待
        while(_task_queue.empty())
        {
            //释放锁，并等待生产者通知可以消费
            pthread_cond_wait(&_c_cond, &_mutex);
        }
        //有数据了，取出数据
        *task = _task_queue.front();
        _task_queue.pop();
        //通知生产者可以生产了
        pthread_cond_signal(&_p_cond);
        //解锁
        pthread_mutex_unlock(&_mutex);
    }
    //析构函数
    ~BlockQueue()
    {
        //锁和条件变量的销毁
        pthread_mutex_destroy(&_mutex);
        pthread_cond_destroy(&_c_cond);
        pthread_cond_destroy(&_p_cond);
    }
};